#include <iostream>

#include "Misc.h"
#include "EbFuncs.h"

using namespace std;

uint userStrToSnesAddr(const char* str, map<string, int> &labels) {
  if ((str[0] == '0') && (str[1] == 'x'))
    return toSnesPointer(atoi_b(str+2, 16));
  else if (str[0] == '$')
    return atoi_b(str+1, 16);
  else { // CCscript label
    map<string, int>::iterator it = labels.find(str);
    if (it != labels.end())
      return toSnesPointer(it->second);
    else {
      cerr << endl << "Invalid label: " << str << endl;
      return 0;
    }
  }
}

uint toRegPointer(uint ptr) {
  ptr += 0x200;
  if (ptr >= 0xC00200)
    return ptr - 0xC00000;
  else
    return ptr;
}

uint toSnesPointer(uint ptr) {
  ptr -= 0x200;
  if (ptr >= 0x400000)
    return ptr;
  else
   return ptr + 0xC00000;
}

// from EB to ASCII, taken from JHack
char toRegChar(uchar c) {
  if (c < 80) // From JHack, don't ask me
    return '\0';
  return c - 0x30;
}

// from ASCII to EB, taken from JHack
uchar toGameChar(char c) {
  if (c < 32)
    return '\0';
  return c + 0x30;
}
  
void toRegString(char* str) {
  int i = 0;
  while (str[i] != '\0') {
    str[i] = toRegChar(str[i]);
    i++;
  }
}

// regStr should be len+1 chars long, last char will be set to '/0'
// AKA, len is the amount of bytes to read from the ROM
void readRegString(Rom &rom, char* regStr, int len) {
  uchar gameStr[len+1];
  rom.read(gameStr, len);
  gameStr[len] = '\0';
  for (int i = 0; i < len+1; i++) {
    regStr[i] = toRegChar(gameStr[i]);
  }
}

void writeRegString(Rom &rom, char* regStr, int len) {
  int i;
  for (i=0; i<len; i++) {
    if (regStr[i] == '\0') {
      rom.write('\0');
      i++;
      break;
    }
    rom.write(toGameChar(regStr[i]));
  }
  for (; i<len; i++)
    rom.write('\0');
}

// Read/Write 2Bpp/4Bpp code adapted from JHack
// Reads a 2BPP 8x8 image
void read2BppImage(uchar* data, uchar* image, int x, int y, int w, int bitOffset) {
  int i, j, k;
  for (i=0; i<8; i++) {
    for (k=0; k<2; k++) {
      for (j=0; j<8; j++) {
        image[((y+i)*w)+(x+7-j)] |= (((*data) & (1 << j)) >> j) << (k+bitOffset);
      }
      data++;
    }
  }
}

// Reads a 4BPP 8x8 image
void read4BppImage(uchar* data, uchar* image, int x, int y, int w, int bitOffset) {
  read2BppImage(data, image, x, y, w, bitOffset);
  read2BppImage(data+16, image, x, y, w, bitOffset+2);
}

void read4BppImage(uchar* data, uchar* image, int bitOffset) {
  read4BppImage(data, image, 0, 0, 8, bitOffset);
}

void write2BppImage(uchar* image, uchar* data, int x, int y, int w, int bitOffset) {
  int i, j, k;
  for (i=0; i<8; i++) {
    for (k=0; k<2; k++) {
      *data = 0;
      for (j=0; j<8; j++)
        *data |= ((image[((y+i)*w)+(x+7-j)] & (1 << (k+bitOffset))) >> (k+bitOffset)) << j;
      data++;
    }
  }
}

void write4BppImage(uchar* image, uchar* data, int x, int y, int w, int bitOffset) {
  write2BppImage(image, data, x, y, w, bitOffset);
  write2BppImage(image, data+16, x, y, w, bitOffset+2);
}

void write4BppImage(uchar* image, uchar* data, int bitOffset) {
  write4BppImage(image, data, 0, 0, 8, bitOffset);
}

bool writeToFree(Rom &rom, uchar* data, uint dataLen, uint pointerLoc, uint pointerLen) {
  uint addr = rom.getFreeRange(dataLen);
  if (addr) {
    rom.write(data, addr, dataLen);
    rom.writeMulti(toSnesPointer(addr), pointerLoc, pointerLen);
    return true;
  } else
    return false;
}
